// @nestjs/passport 作用是提供了一个 passport 模块，
// 它可以用来创建 passport 策略，并且可以在其他模块中使用。 例如，
// 在 AppModules 中，我们可以使用如下代码来创建一个 passport 策略：
// import { PassportModule } from '@nestjs/passport';
// import { UsersModule } from './users.module';
// @Module({
//   imports: [
//     PassportModule.register({ defaultStrategy: 'jwt' }),
//     UsersModule,
//   ],
// })
// export class AuthModule {}
//
// 在这里，我们使用了 PassportModule.register() 方法来创建一个 passport 策略，
// 并且使用了默认的策略名称，即 jwt。
//
// PassportStrategy 是 passport 的策略类，
import { PassportStrategy } from '@nestjs/passport';
// passport-jwt 作用是解析token，并将解析后的用户信息放入req.user
// Strategy 作用是实现策略的接口， ExtractJwt 作用是解析token
import { Strategy, ExtractJwt } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { UnauthorizedException, Injectable } from '@nestjs/common';

// import { AuthService } from './auth.service';
import { UsersService } from '@/modules/users/users.service';

@Injectable()
export class AuthStrategy extends PassportStrategy(Strategy) {
  /**
   * 这里的构造函数向父类传递了授权时必要的参数，在实例化时，父类会得知授权时，客户端的请求必须使用 Authorization 作为请求头，
   * 而这个请求头的内容前缀也必须为 Bearer，在解码授权令牌时，使用秘钥 secretOrKey: 'secretKey' 来将授权令牌解码为创建令牌时的 payload。
   */
  constructor(private readonly userService: UsersService, private readonly config: ConfigService) {
    console.log(config.get('jwt.secrectKey'));
    super({
      // jwtFromRequest 是一个函数，用于从请求头中获取授权令牌，这里使用 Authorization 作为请求头，
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: config.get('jwt.secrectKey'),
    });
  }

  /**
   * validate 方法实现了父类的抽象方法，在解密授权令牌成功后，即本次请求的授权令牌是没有过期的，
   * 此时会将解密后的 payload 作为参数传递给 validate 方法，这个方法需要做具体的授权逻辑，比如这里我使用了通过用户名查找用户是否存在。
   * 当用户不存在时，说明令牌有误，可能是被伪造了，此时需抛出 UnauthorizedException 未授权异常。
   * 当用户存在时，会将 user 对象添加到 req 中，在之后的 req 对象中，可以使用 req.user 获取当前登录用户。
   */
  async validate(payload: { id: string }) {
    const user = await this.userService.findOneById(payload.id);
    // 如果用用户信息，代表 token 没有过期，没有则 token 已失效
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}
